/*
 /	This file is part of Answerkeys Disabler
 /
 /	Answerkeys Disabler is free software: you can redistribute it and/or modify
 /	it under the terms of the GNU General Public License as published by
 /	the Free Software Foundation, either version 3 of the License, or
 /	(at your option) any later version.
 /
 /	Answerkeys Disabler is distributed in the hope that it will be useful,
 /	but WITHOUT ANY WARRANTY; without even the implied warranty of
 /	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /	GNU General Public License for more details.
 /
 /	You should have received a copy of the GNU General Public License
 /	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /
 /	Copyright 2008 Daan van Dijk (drvdijk@gmail.com)
 */

#include "stdafx.h"
#include "TAPIDevice.h"
#include "touchpanel.h"

#define EXIT_EVENT_NAME _T("AnswerkeysDisabler_Exit_Event")

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{

	HANDLE g_hExitEvent = CreateEvent(NULL, FALSE, FALSE, EXIT_EVENT_NAME);

	// Unload option
	if(0 == _tcscmp(_T("/u"), lpCmdLine)) {
		SetEvent(g_hExitEvent);
		CloseHandle(g_hExitEvent);
		return 0;
	}

	// Check if the program is already running
	bool bIsRunning = false;
	if(GetLastError() == ERROR_ALREADY_EXISTS) {
		bIsRunning = true;
	}
	if(bIsRunning) {
		if(IDYES == MessageBox(NULL, L"Answerkeys Disabler is already enabled. Disable it?", L"Answerkeys Disabler", MB_YESNO | MB_ICONQUESTION)) {
			SetEvent(g_hExitEvent);
		}
		CloseHandle(g_hExitEvent);
		return 0;
	}

	// Load settings from registry
	bool unlockWithStylus = true;
	bool alwaysTurnOffDevice = false;
	bool turnOffDeviceIfItWasJustOn = true;
	bool emulatePowerButton = true;
	bool lockOnlyFromStandby = false;
	bool lockCallsDuringCalls = false;
	HKEY m_hRegistryKey;
	DWORD dwDisposition;
	DWORD nResult = RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\DRvDijk\\AnswerkeysDisabler"), 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &m_hRegistryKey, &dwDisposition);
	if (nResult == ERROR_SUCCESS) {
		DWORD dwType;
		DWORD dwData;
		DWORD dwDataSize = sizeof(dwData);
		nResult = RegQueryValueEx(m_hRegistryKey, L"UnlockWithStylusEnabled", NULL, &dwType, (LPBYTE)&dwData, &dwDataSize);
		if(nResult == ERROR_SUCCESS && dwType == REG_DWORD && dwDataSize == sizeof(dwData)) {
			unlockWithStylus = dwData == 1;
		}
		nResult = RegQueryValueEx(m_hRegistryKey, L"TurnOffScreenMode", NULL, &dwType, (LPBYTE)&dwData, &dwDataSize);
		if (nResult == ERROR_SUCCESS && dwType == REG_DWORD && dwDataSize == sizeof(dwData)) {
			alwaysTurnOffDevice = dwData == 1;
			turnOffDeviceIfItWasJustOn = dwData == 2;
		}
		nResult = RegQueryValueEx(m_hRegistryKey, L"TurnOffScreenMethod", NULL, &dwType, (LPBYTE)&dwData, &dwDataSize);
		if (nResult == ERROR_SUCCESS && dwType == REG_DWORD && dwDataSize == sizeof(dwData)) {
			emulatePowerButton = dwData == 1;
		}
		nResult = RegQueryValueEx(m_hRegistryKey, L"LockOnlyFromStandby", NULL, &dwType, (LPBYTE)&dwData, &dwDataSize);
		if(nResult == ERROR_SUCCESS && dwType == REG_DWORD && dwDataSize == sizeof(dwData)) {
			lockOnlyFromStandby = dwData == 1;
		}
		nResult = RegQueryValueEx(m_hRegistryKey, L"LockCallsDuringCalls", NULL, &dwType, (LPBYTE)&dwData, &dwDataSize);
		if(nResult == ERROR_SUCCESS && dwType == REG_DWORD && dwDataSize == sizeof(dwData)) {
			lockCallsDuringCalls = dwData == 1;
		}
	}
	RegCloseKey(m_hRegistryKey);

	// Initialize TAPI device
	CTAPIDevice m_cDevice;
	if(!m_cDevice.Initialize(NULL) || !m_cDevice.OpenLine()) {
		MessageBox(GetForegroundWindow(), L"Sorry, Answerkeys Disabler failed initialization", L"Error", MB_OK | MB_ICONERROR);
		CloseHandle(g_hExitEvent);
		return 1;
	}

	// Get a touch panel handle, create hiding events
	CTouchPanel m_cTouchPanel;
	if (!m_cTouchPanel.isLoaded()) {
		MessageBox(GetForegroundWindow(), L"Sorry, Answerkeys Disabler failed initialization", L"Error", MB_OK | MB_ICONERROR);
		CloseHandle(g_hExitEvent);
		return 1;
	}
	HANDLE g_hIncomingCallEvent = CreateEvent(NULL, FALSE, FALSE, INCOMING_CALL_EVENT_NAME);
	HANDLE g_hCallConnectedEvent = CreateEvent(NULL, FALSE, FALSE, CALL_CONNECTED_EVENT_NAME);
	HANDLE g_hCallEndedEvent = CreateEvent(NULL, FALSE, FALSE, CALL_ENDED_EVENT_NAME);
	bool currentlyLocking = false;

	int arrEventsCount = 4;
	HANDLE arrEvents[6] = {g_hExitEvent, g_hIncomingCallEvent, g_hCallEndedEvent, g_hCallConnectedEvent, NULL, NULL};

	// Get registry changed handle to catch stylus changes
	HANDLE g_hStylusChanged = NULL;
	if (unlockWithStylus) {
		if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _T("ControlPanel\\Keybd"), 0, 0, &m_hRegistryKey)) {
			g_hStylusChanged = CeFindFirstRegChange(m_hRegistryKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET);
			if (g_hStylusChanged != INVALID_HANDLE_VALUE) {
				CeFindNextRegChange(g_hStylusChanged);
			}
		}
		RegCloseKey(m_hRegistryKey);
		arrEvents[arrEventsCount++] = g_hStylusChanged;
	}


	// Start listening for power state changes
	HANDLE g_hPMNotifyQ;
	HANDLE hPwrNotify;
	if (lockOnlyFromStandby || turnOffDeviceIfItWasJustOn) {
		MSGQUEUEOPTIONS msgQpm = {0}; 
		msgQpm.dwSize = sizeof(MSGQUEUEOPTIONS);
		msgQpm.dwFlags = MSGQUEUE_NOPRECOMMIT;
		msgQpm.dwMaxMessages = 0;
		msgQpm.cbMaxMessage = sizeof(POWER_BROADCAST_POWER_INFO) + sizeof(POWER_BROADCAST) + MAX_PATH;
		msgQpm.bReadAccess = TRUE;
		g_hPMNotifyQ = CreateMsgQueue(_T("PowerMsgQ"), &msgQpm);;
		hPwrNotify = RequestPowerNotifications(g_hPMNotifyQ, PBT_TRANSITION);
		arrEvents[arrEventsCount++] = g_hPMNotifyQ;
	}
	DWORD lastPowerOn = GetTickCount();
	bool aSleep = false;



	// Count the number of active calls
	int activeCalls = 0;

	// Pack the waiting events together for the loop

	bool doContinue = true;
	bool disableScreenAfterEnablingTouchScreen = false;
	bool justOn = false;

	while (doContinue) {
		DWORD dwWait;
		dwWait = WaitForMultipleObjects(arrEventsCount, arrEvents, FALSE, currentlyLocking ? 100 : INFINITE);
		switch (dwWait) {
			case WAIT_OBJECT_0:  // g_hExitEvent
				doContinue = false;
				m_cTouchPanel.SetDisabled(false);
				break;
			case WAIT_OBJECT_0 + 1: // g_hIncomingCallEvent
				justOn = (GetTickCount() - lastPowerOn < 1000);
				if (!lockOnlyFromStandby || justOn) {
					if (activeCalls == 0 || lockCallsDuringCalls) {
						m_cTouchPanel.SetDisabled(true);
						currentlyLocking = true;
					}
				}
				activeCalls++;
				if (justOn) {
					disableScreenAfterEnablingTouchScreen = justOn || aSleep;
				}
				break;
			case WAIT_OBJECT_0 + 2: // g_hCallEndedEvent
				activeCalls--;
			case WAIT_OBJECT_0 + 3: // g_hCallConnectedEvent
				m_cTouchPanel.SetDisabled(false);
				if (alwaysTurnOffDevice || turnOffDeviceIfItWasJustOn) {
					if (turnOffDeviceIfItWasJustOn) {
						DWORD tickDifference = GetTickCount() - lastPowerOn;
						if (!currentlyLocking && tickDifference > 1000) {
							disableScreenAfterEnablingTouchScreen = false;
						}
						else if (tickDifference < 1000) {
							disableScreenAfterEnablingTouchScreen = true;
						}
					}
					if (alwaysTurnOffDevice || disableScreenAfterEnablingTouchScreen) {
						aSleep = true;
						if (emulatePowerButton) {
							// Code from http://msdn.microsoft.com/en-us/library/ms832325.aspx
							keybd_event(VK_OFF, 0, 0, 0);
							keybd_event(VK_OFF, 0, KEYEVENTF_KEYUP, 0);
							Sleep(60);
						} else {
							SetSystemPowerState(NULL, POWER_STATE_IDLE, NULL);
						}
					}
				}
				currentlyLocking = false;
				break;
			case WAIT_OBJECT_0 + 4: // g_hPMNotifyQ || g_hStylusChanged
			case WAIT_OBJECT_0 + 5: // g_hPMNotifyQ || g_hStylusChanged
				if (arrEvents[dwWait - WAIT_OBJECT_0] == g_hPMNotifyQ) {
					BYTE  buffer[sizeof(POWER_BROADCAST) + sizeof(POWER_BROADCAST_POWER_INFO)];
					DWORD dwRead;
					DWORD dwFlags;
					if(ReadMsgQueue(g_hPMNotifyQ, (LPVOID)buffer, sizeof(buffer), &dwRead, INFINITE, &dwFlags)) {
						POWER_BROADCAST* pPower = (POWER_BROADCAST*)buffer;
						if (pPower->Message == PBT_TRANSITION) {
							std::wstring powerState = pPower->SystemPowerState;
							if (powerState == L"unattended") {
								aSleep = true;
							}
							else if (powerState == L"on") {
								if (aSleep) lastPowerOn = GetTickCount();
								aSleep = false;
							}
							else if (powerState == L"backlightoff") {
								aSleep = false;
							}
						}
					}
				}
				else if (arrEvents[dwWait - WAIT_OBJECT_0] == g_hStylusChanged) {
					currentlyLocking = false;
					disableScreenAfterEnablingTouchScreen = false;
					m_cTouchPanel.SetDisabled(false);
					CeFindNextRegChange(g_hStylusChanged);
				}
				break;
			case WAIT_TIMEOUT:
				m_cTouchPanel.SetDisabled(true);
				break;
		} // end switch
	} // end loop

	// Clean up
	if (lockOnlyFromStandby || turnOffDeviceIfItWasJustOn) {
		StopPowerNotifications(hPwrNotify);
		CloseMsgQueue(g_hPMNotifyQ);
	}
	if (unlockWithStylus) {
		CloseHandle(g_hStylusChanged);
	}
	CloseHandle(g_hIncomingCallEvent);
	CloseHandle(g_hCallConnectedEvent);
	CloseHandle(g_hCallEndedEvent);
	m_cDevice.Shutdown();
	CloseHandle(g_hExitEvent);

	return 0;
}
